﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Twodi.Aspect.Interfaces;
using Twodi.Interfaces;
using Microsoft.Xna.Framework;
using Twodi.Aspect.Implemented;
using Twodi.Behavior.Implemented;
using System.ComponentModel;
using Microsoft.Xna.Framework.Graphics;
using Twodi.Input;
using Twodi.Input.Events;
using Twodi.Arguments;

namespace Twodi.Implemented
{
    public abstract class AbstractContainer<T> : AbstractRenderable, IContainerAspect<T>
    {
        public AbstractContainer(Game game)
            : base(game)
        {
            ContainerProperties = new ContainerAspect<T>();
            ContainerBehavior = new BasicContainerBehavior<T>(this);
        }

        /// <summary>
        /// Inicializar tela e componentes internos.
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Initialize()
        {
            if (ContainerBehavior != null) ContainerBehavior.Initialize();
            base.Initialize();
        }

        /// <summary>
        /// Atualizar tela e componentes internos.
        /// </summary>
        /// <param name="gameTime">Tempo atual de jogo.</param>
        public override void Update(GameTime gameTime)
        {
            if (!Enabled) return;
            if (ContainerBehavior != null) ContainerBehavior.Update(gameTime);
            base.Update(gameTime);
        }

        /// <summary>
        /// Renderizar tela e componentes internos.
        /// </summary>
        /// <param name="gameTime">Tempo atual de jogo.</param>
        public override void Draw(GameTime gameTime)
        {
            if (!Visible) return;
            if (ContainerBehavior != null)
            {
                onContainerPreRender(new RenderEventArgs<SpriteBatch>(SpriteBatch));
                ContainerBehavior.Draw(gameTime);
                onContainerPosRender(new RenderEventArgs<SpriteBatch>(SpriteBatch));
            };
            base.Draw(gameTime);
        }

        public override bool Handle(MouseInput input)
        {
            if (ContainerBehavior != null)
            {
                onContainerPreHandle(new InputEventArgs<MouseInput>(input));
                ContainerBehavior.Handle(input);
                onContainerPosHandle(new InputEventArgs<MouseInput>(input));
            };
            return base.Handle(input);
        }

        public override bool Handle(KeyboardInput input)
        {
            if (ContainerBehavior != null)
            {
                onContainerPreHandle(new InputEventArgs<KeyboardInput>(input));
                ContainerBehavior.Handle(input);
                onContainerPosHandle(new InputEventArgs<KeyboardInput>(input));
            };
            return base.Handle(input);
        }

        /// <summary>
        /// Adicionar multiplos componentes ao container.
        /// </summary>
        /// <param name="components">Componentes a serem adicionados ordenadamente.</param>
        public virtual void Add(params T[] components)
        {
            this.ContainerProperties.Add(components);
        }

        /// <summary>
        /// Componentes do container.
        /// </summary>
        public BindingList<T> Components { get { return this.ContainerProperties.Components; } }

        /// <summary>
        /// 
        /// </summary>
        public IContainer<T> ContainerProperties
        {
            get;
            set;
        }

        /// <summary>
        /// 
        /// </summary>
        public AbstractContainerBehavior<T> ContainerBehavior
        {
            get;
            protected set;
        }

        /// <summary>
        /// Evento invocado antes de ser iniciado o Draw();
        /// </summary>
        public event EventHandler<RenderEventArgs<SpriteBatch>> ContainerPreRender;

        /// <summary>
        /// Evento invocado após ser finalizado o Draw();
        /// </summary>
        public event EventHandler<RenderEventArgs<SpriteBatch>> ContainerPosRender;

        /// <summary>
        /// Evento invocado antes de ser iniciado o Handle();
        /// </summary>
        public event EventHandler<InputEventArgs<MouseInput>> ContainerPreMouseHandle;

        /// <summary>
        /// Evento invocado após ser finalizado o Handle();
        /// </summary>
        public event EventHandler<InputEventArgs<MouseInput>> ContainerPosMouseHandle;

        /// <summary>
        /// Evento invocado antes de ser iniciado o Handle();
        /// </summary>
        public event EventHandler<InputEventArgs<KeyboardInput>> ContainerPreKeyboardHandle;

        /// <summary>
        /// Evento invocado após ser finalizado o Handle();
        /// </summary>
        public event EventHandler<InputEventArgs<KeyboardInput>> ContainerPosKeyboardHandle;

        /// <summary>
        /// Método invocado antes de ser iniciado o Draw();
        /// </summary>
        protected virtual void onContainerPreRender(RenderEventArgs<SpriteBatch> e)
        {
            if (ContainerPreRender != null)
            {
                ContainerPreRender(this, e);
            }
        }

        /// <summary>
        /// Método invocado após de ser finalizado o Draw();
        /// </summary>
        protected virtual void onContainerPosRender(RenderEventArgs<SpriteBatch> e)
        {
            if (ContainerPosRender != null)
            {
                ContainerPosRender(this, e);
            }
        }

        /// <summary>
        /// Método invocado antes de ser iniciado o Handle();
        /// </summary>
        protected virtual void onContainerPreHandle(InputEventArgs<MouseInput> e)
        {
            if (ContainerPreMouseHandle != null)
            {
                ContainerPreMouseHandle(this, e);
            }
        }

        /// <summary>
        /// Método invocado após de ser finalizado o Handle();
        /// </summary>
        protected virtual void onContainerPosHandle(InputEventArgs<MouseInput> e)
        {
            if (ContainerPosMouseHandle != null)
            {
                ContainerPosMouseHandle(this, e);
            }
        }
        
        /// <summary>
        /// Método invocado antes de ser iniciado o Handle();
        /// </summary>
        protected virtual void onContainerPreHandle(InputEventArgs<KeyboardInput> e)
        {
            if (ContainerPreKeyboardHandle != null)
            {
                ContainerPreKeyboardHandle(this, e);
            }
        }

        /// <summary>
        /// Método invocado após de ser finalizado o Handle();
        /// </summary>
        protected virtual void onContainerPosHandle(InputEventArgs<KeyboardInput> e)
        {
            if (ContainerPosKeyboardHandle != null)
            {
                ContainerPosKeyboardHandle(this, e);
            }
        }
    }

    public class BasicContainerBehavior<T> : AbstractContainerBehavior<T>
    {
        public BasicContainerBehavior(IContainerAspect<T> component)
            : base(component)
        {
            
        }

        public override void Initialize()
        {
            for (int i = 0; i < this.Component.ContainerProperties.Components.Count; i++)
            {
                IGameComponent component = this.Component.ContainerProperties.Components[i] as IGameComponent;
                if (component != null)
                {
                    component.Initialize();
                }
            }
        }

        public override void Update(GameTime gameTime)
        {
            for (int i = 0; i < this.Component.ContainerProperties.Components.Count; i++)
            {
                IUpdateable component = this.Component.ContainerProperties.Components[i] as IUpdateable;
                if (component != null)
                {
                    if (component.Enabled) component.Update(gameTime);
                }
            }
        }

        public override void Draw(GameTime gameTime)
        {
            for (int i = 0; i < this.Component.ContainerProperties.Components.Count; i++)
            {
                IDrawable component = this.Component.ContainerProperties.Components[i] as IDrawable;
                if (component != null)
                {
                    if (component.Visible) component.Draw(gameTime);
                }
            }
        }

        public override bool Handle(KeyboardInput input)
        {
            for (int i = this.Component.ContainerProperties.Components.Count - 1; i >= 0; i--)
            {
                IKeyboardHandler component = this.Component.ContainerProperties.Components[i] as IKeyboardHandler;
                if (component != null)
                {
                    if (!component.Handle(input))
                    {
                        break;
                    }
                }
            }
            return true;
        }

        public override bool Handle(MouseInput input)
        {
            for (int i = this.Component.ContainerProperties.Components.Count - 1; i >= 0; i--)
            {
                IMouseHandler component = this.Component.ContainerProperties.Components[i] as IMouseHandler;
                if (component != null)
                {
                    if (!component.Handle(input))
                    {
                        break;
                    }
                }
            }
            return true;
        }
    }
}
